﻿using System;
using System.Configuration;
using System.Linq;
using System.Threading;
using agsXMPP;
using agsXMPP.protocol.client;
using agsXMPP.protocol.iq.roster;
using agsXMPP.Xml.Dom;
using GDataDB;
using GDataDB.Linq;
using Google.GData.Client;

namespace xmpp2gsp {
	public class Program {
		private static readonly ManualResetEvent quitEvent = new ManualResetEvent(false);
		private static ITable<Log> table;
		private static bool IsPunchedIn;

		private static string GoogleUsername {
			get { return ConfigurationManager.AppSettings["Google.Username"]; }
		}

		private static string GooglePassword {
			get { return ConfigurationManager.AppSettings["Google.Password"]; }
		}

		private static string JabberJid {
			get { return ConfigurationManager.AppSettings["Jabber.JID"]; }
		}

		private static string JabberPassword {
			get { return ConfigurationManager.AppSettings["Jabber.Password"]; }
		}

		private static string StatusCommand {
			get { return ConfigurationManager.AppSettings["Command.Status"]; }
		}

		private static string QuitCommand {
			get { return ConfigurationManager.AppSettings["Command.Quit"]; }
		}

		private static string PunchInCommand {
			get { return ConfigurationManager.AppSettings["Command.PunchIn"]; }
		}

		private static string PunchOutCommand {
			get { return ConfigurationManager.AppSettings["Command.PunchOut"]; }
		}

		private static string ListenTo {
			get { return ConfigurationManager.AppSettings["ListenTo"]; }
		}

		private static void Main(string[] args) {
			try {
				Console.WriteLine("Logging in to google docs...");
				var client = new DatabaseClient(GoogleUsername, GooglePassword);
				const string dbName = "Log";
				var db = client.GetDatabase(dbName) ?? client.CreateDatabase(dbName);
				const string tableName = "log1";
				table = db.GetTable<Log>(tableName) ?? db.CreateTable<Log>(tableName);

				Console.WriteLine("Logging in to jabber...");

				var jid = new Jid(JabberJid);

				var conn = new XmppClientConnection {
					Username = jid.User,
					Server = jid.Server,
					Password = JabberPassword,
					AutoAgents = true,
					AutoPresence = true,
					AutoRoster = true,
					AutoResolveConnectServer = true,
				};

				conn.OnMessage += conn_OnMessage;
				conn.OnError += conn_OnError;
				conn.OnLogin += conn_OnLogin;
				conn.OnAuthError += conn_OnAuthError;
				conn.OnRosterItem += conn_OnRosterItem;
				conn.OnPresence += conn_OnPresence;
				conn.Open();
				quitEvent.WaitOne();
				conn.Close();
			} catch (AuthenticationException e) {
				Console.WriteLine("Google Auth exception: {0}", e.Message);
			}
		}

		private static void conn_OnPresence(object sender, Presence pres) {
			var conn = (XmppClientConnection)sender;
			if (pres.From.Bare == ListenTo && pres.Type == PresenceType.subscribe) {
				conn.PresenceManager.ApproveSubscriptionRequest(pres.From);
				conn.PresenceManager.Subcribe(pres.From);
			}
			Console.WriteLine("Presence from {0} to {1} type {2}", pres.From, pres.To, pres.Type);
		}

		private static void conn_OnRosterItem(object sender, RosterItem item) {
			Console.WriteLine("Roster {0} subscription {1}", item.Jid.Bare, item.Subscription);
		}

		private static void conn_OnAuthError(object sender, Element e) {
			Console.WriteLine("Invalid jabber credentials!");
			quitEvent.Set();
		}

		private static void conn_OnLogin(object sender) {
			var lastStatus = GetLastPunchLog();
			IsPunchedIn = lastStatus != null && lastStatus.Entry == PunchInCommand;
			var conn = (XmppClientConnection)sender;
			conn.PresenceManager.Subcribe(new Jid(ListenTo));
			conn.SendMyPresence();
			Console.WriteLine("Ready. Waiting for messages...");
		}

		private static void conn_OnError(object sender, Exception ex) {
			Console.WriteLine(ex);
		}

		private static Log GetLastPunchLog() {
			var q = table.AsQueryable()
				.Where(r => r.Entry == PunchInCommand || r.Entry == PunchOutCommand)
				.OrderByDescending(r => r.Timestamp)
				.Take(1)
				;
			var st = q.ToList();
			return st.FirstOrDefault();
		}

		public static Action<T> A<T>(Action<T> a) {
			return a;
		}

		public static Action A(Action a) {
			return a;
		}

		private static void conn_OnMessage(object sender, Message msg) {
			if (msg.Body == null)
				return;
			if (msg.From.Bare != ListenTo) {
				return;
			}
			var conn = (XmppClientConnection) sender;
			var answer = A((string m) => { conn.Send(new Message(msg.From, MessageType.chat, m)); });
			var add = A(() => {
				Console.WriteLine("From {0}: {1}", msg.From, msg.Body);
				table.Add(new Log {Entry = msg.Body});
			});
			if (msg.Body == QuitCommand) {
				if (IsPunchedIn)
					table.Add(new Log {Entry = PunchOutCommand});
				quitEvent.Set();
				return;
			}
			if (msg.Body == StatusCommand) {
				var status = GetLastPunchLog() ?? new Log {Entry = "nothing logged yet"};
				answer(string.Format("{0} @{1}", status.Entry, status.Timestamp));
				return;
			}
			if (msg.Body == PunchInCommand) {
				if (IsPunchedIn) {
					answer("Already punched in");
				} else {
					IsPunchedIn = true;
					add();
				}
				return;
			}
			if (msg.Body == PunchOutCommand) {
				if (!IsPunchedIn) {
					answer("Already punched out");
				} else {
					IsPunchedIn = false;
					add();
				}
				return;
			}
			if (!IsPunchedIn) {
				IsPunchedIn = true;
				table.Add(new Log {Entry = PunchInCommand});
			}
			add();
		}
	}
}